package utils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
)

// AesEncrypt
// Aes加密
func AesEncrypt(plaintext string, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	plainBytes := []byte(plaintext)
	// Using PKCS#7 Padding
	padding := block.BlockSize() - len(plainBytes)%block.BlockSize()
	paddedPlainBytes := append(plainBytes, bytes.Repeat([]byte{byte(padding)}, padding)...)

	encrypted := make([]byte, aes.BlockSize+len(paddedPlainBytes))
	iv := encrypted[:aes.BlockSize]
	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(encrypted[aes.BlockSize:], paddedPlainBytes)

	return hex.EncodeToString(encrypted), nil
}

// AesDecrypt
// Aes解密
func AesDecrypt(encryptedHex string, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	encrypted, err := hex.DecodeString(encryptedHex)
	if err != nil {
		return "", err
	}

	if len(encrypted) < aes.BlockSize {
		return "", fmt.Errorf("encrypted data too short")
	}
	iv := encrypted[:aes.BlockSize]
	encrypted = encrypted[aes.BlockSize:]

	if len(encrypted)%block.BlockSize() != 0 {
		return "", fmt.Errorf("encrypted data is not a multiple of block size")
	}

	decrypted := make([]byte, len(encrypted))
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(decrypted, encrypted)

	// Unpad using PKCS#7 unpadding method
	padding := decrypted[len(decrypted)-1]
	unpaddedDecrypted := decrypted[:len(decrypted)-int(padding)]

	return string(unpaddedDecrypted), nil
}

// MD5Hash
// 将字符串用MD5编码后返回
func MD5Hash(text string, num int) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	hashToString := hex.EncodeToString(hasher.Sum(nil))

	if num > len(hashToString) || num <= 0 {
		return hashToString
	}
	return hashToString[:num]
}

// MD5HashWithSalt
// 将字符串用MD5编码后返回，并可以添加盐值
func MD5HashWithSalt(text, salt string, num int) string {
	return MD5Hash(text+salt, num)
}

// SHA1Hash
// 将字符串用SHA1编码后返回
func SHA1Hash(text string) string {
	hasher := sha1.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA224Hash
// 将字符串用SHA224编码后返回
func SHA224Hash(text string) string {
	hasher := sha256.New224()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA256Hash
// 将字符串用SHA256编码后返回
func SHA256Hash(text string) string {
	hasher := sha256.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA384Hash
// 将字符串用SHA384编码后返回
func SHA384Hash(text string) string {
	hasher := sha512.New384()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA512Hash
// 将字符串用SHA512编码后返回
func SHA512Hash(text string) string {
	hasher := sha512.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA512_224Hash
// 将字符串用SHA512/224编码后返回
func SHA512_224Hash(text string) string {
	hasher := sha512.New512_224()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// SHA512_256Hash
// 将字符串用SHA512/256编码后返回
func SHA512_256Hash(text string) string {
	hasher := sha512.New512_256()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func getHMAC(text string, key string, hasher func() hash.Hash) string {
	h := hmac.New(hasher, []byte(key))
	h.Write([]byte(text))
	return hex.EncodeToString(h.Sum(nil))
}

// HMACMD5Hash
// 将字符串用HMAC-MD5编码后返回
func HMACMD5Hash(text string, key string) string {
	return getHMAC(text, key, md5.New)
}

// HMACSHA1Hash
// 将字符串用HMAC-SHA1编码后返回
func HMACSHA1Hash(text string, key string) string {
	return getHMAC(text, key, sha1.New)
}

// HMACSHA224Hash
// 将字符串用HMAC-SHA224编码后返回
func HMACSHA224Hash(text string, key string) string {
	return getHMAC(text, key, sha256.New224)
}

// HMACSHA256Hash
// 将字符串用HMAC-SHA256编码后返回
func HMACSHA256Hash(text string, key string) string {
	return getHMAC(text, key, sha256.New)
}

// HMACSHA384Hash
// 将字符串用HMAC-SHA384编码后返回
func HMACSHA384Hash(text string, key string) string {
	return getHMAC(text, key, sha512.New384)
}

// HMACSHA512Hash
// 将字符串用HMAC-SHA512编码后返回
func HMACSHA512Hash(text string, key string) string {
	return getHMAC(text, key, sha512.New)
}

// HMACSHA512_224Hash
// 将字符串用HMAC-SHA512/224编码后返回
func HMACSHA512_224Hash(text string, key string) string {
	return getHMAC(text, key, sha512.New512_224)
}

// HMACSHA512_256Hash
// 将字符串用HMAC-SHA512/256编码后返回
func HMACSHA512_256Hash(text string, key string) string {
	return getHMAC(text, key, sha512.New512_256)
}
